package com.cymal.builder;

import com.cymal.model.RedisServer;
import com.cymal.utils.XmlNodeToRedisNodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 配置文件加载器
 */
@Slf4j
public class RedisFileLoader {

    // 根结点名
    private final String REDIS_CONF = "redis-conf";
    // 集群结点名
    private final String REDIS_COLONY = "redis-colony";
    // 主结点名
    private final String MASTER = "master";
    // 从结点名
    private final String SLAVE = "slave";
    // 过滤字段
    private static List<String> FILTERS = new ArrayList<>();



    /**
     * <h3>读取类路径的文件，返回RedisServerBean</h1>
     * <ol>
     *     <li></li>
     * </ol>
     *
     * @return RedisServerBean
     */
    public List<RedisServer> loadFile() {
        // 创建一个空Bean
        List<RedisServer> redisServers = new ArrayList<>();
        // 开始读取文件
        try (InputStream in = getClass().getResourceAsStream("/cedis.xml")) {
            buildRedisServerList(redisServers, in);
            return redisServers;

        } catch (Exception e) {
            e.printStackTrace();
            return redisServers;
        }
    }


    /**
     * <p>
     *     构建集群集合
     * </p>
     * @param redisServers 集群集合
     * @param in Xml文件输入流
     * @return
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws IOException
     */
    private List<RedisServer> buildRedisServerList(List<RedisServer> redisServers, InputStream in) throws ParserConfigurationException, SAXException, IOException {
        // 没有配置文件
        if (in == null) {
            log.info("没有载入cedis配置文件!");
            return redisServers;
        }
        // 创建XML解析器工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = factory.newDocumentBuilder();
        // 创建一个Document对象
        Document doc = db.parse(in);
        // 获得根节点集合
        NodeList redisConfList = doc.getElementsByTagName(REDIS_CONF);
        checkRedisConf(redisConfList);
        // 获得根节点
        Node redisConf = redisConfList.item(0);
        // 获得集群集合
        NodeList colonies = redisConf.getChildNodes();
        // 遍历集群节点
        for (int i = 0; i < colonies.getLength(); i++) {
            // 获得redis集群节点
            Node colony = colonies.item(i);
            // 校验集群节点
            if (FILTERS.contains(colony.getNodeName())) {
                continue;
            }
            checkColoy(colony);
            // 获得主节点个数
            int masterNode = 0;
            // 获得Redis节点
            NodeList redisNodes = colony.getChildNodes();
            checkRedisNodes(masterNode, redisNodes);
            RedisServer redisServer = new RedisServer();
            redisServer.setName(XmlNodeToRedisNodeUtils.toName(colony, "name"));
            for (int j = 0; j < redisNodes.getLength(); j++) {
                if (FILTERS.contains(redisNodes.item(j).getNodeName())) {
                    continue;
                }
                if (redisNodes.item(j).getNodeName().equalsIgnoreCase(MASTER)) {
                    redisServer.setMaster(XmlNodeToRedisNodeUtils.toRedisNode(redisNodes.item(j)));
                }
                if (redisNodes.item(j).getNodeName().equalsIgnoreCase(SLAVE)) {
                    redisServer.addRedisNode(XmlNodeToRedisNodeUtils.toRedisNode(redisNodes.item(j)));
                }
            }
            redisServers.add(redisServer);

        }
        return redisServers;
    }

    private void checkRedisNodes(int masterNode, NodeList redisNodes) {
        for (int j = 0; j < redisNodes.getLength(); j++) {
            if (FILTERS.contains(redisNodes.item(j).getNodeName())) {
                continue;
            }
            if (!redisNodes.item(j).getNodeName().equalsIgnoreCase(MASTER)
                    && !redisNodes.item(j).getNodeName().equalsIgnoreCase(SLAVE)) {
                log.info("主节点名必须为：master，子节点名必须为：slave");
                throw new RuntimeException();
            }
            if (redisNodes.item(j).getNodeName().equalsIgnoreCase(MASTER)) {
                masterNode++;
                if (masterNode > 1) {
                    log.info("一个集群只能有一个主节点");
                    throw new RuntimeException();
                }
            }
        }
        if (masterNode == 0) {
            log.info("一个集群节点至少要有一个主节点");
            throw new RuntimeException();
        }
    }

    private void checkColoy(Node colony) {
        if (!colony.getNodeName().equalsIgnoreCase(REDIS_COLONY)) {
            log.info("集群节点必须为redis-colony");
            throw new RuntimeException();
        }
        if (colony.getChildNodes().getLength() == 0) {
            log.info("至少有一个主节点!");
            throw new RuntimeException();
        }
    }

    private void checkRedisConf(NodeList redisConfList) {
        // 根节点校验
        if (redisConfList == null) {
            log.info("根节点必须为redis-conf");
            throw new RuntimeException();
        }
        if (redisConfList.getLength() != 1) {
            log.info("有且仅有一个根节点!");
            throw new RuntimeException();
        }
        if (redisConfList.item(0).getChildNodes().getLength() == 0) {
            log.info("根节点下至少有一个集群节点");
            throw new RuntimeException();
        }
    }


    /**
     * 添加过滤字段
     */
    static {
        FILTERS.add("#text");
        FILTERS.add("#comment");
    }


}
